﻿using System;
using System.Runtime.InteropServices;
using System.Text;

namespace UtilZ.Dotnet.WindowsDesktopEx.NativeMethod
{
    /// <summary>
    /// 枚举窗口回调委托(返回true表示继续枚举,返回false表示结束枚举)
    /// </summary>
    /// <param name="hWnd"></param>
    /// <param name="lParam"></param>
    /// <returns>返回true表示继续枚举,返回false表示结束枚举</returns>
    internal delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

    /// <summary>
    /// 桌面系统win32方法
    /// </summary>
    internal class DesktopNativeMethods
    {
        private const string DLL_USER32 = "user32.dll";
        private const string DLL_SHELL32 = "shell32.dll";


        /// <summary>
        /// 枚举系统中所有顶级窗口,成功返回true,失败返回false
        /// </summary>
        /// <param name="lpEnumFunc">指向回调函数的指针,用于处理每个子窗口</param>
        /// <param name="lParam">传递给回调函数的参数,可以任意值,常用于传递上下文信息</param>
        /// <returns></returns>
        [DllImport(DLL_USER32)]
        internal static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);

        /// <summary>
        /// 枚举指定父窗口的所有子窗口,成功返回true,失败返回false
        /// </summary>
        /// <param name="hWndParent">要枚举的父窗口句柄</param>
        /// <param name="lpEnumFunc">指向回调函数的指针,用于处理每个子窗口</param>
        /// <param name="lParam">传递给回调函数的参数,可以任意值,常用于传递上下文信息</param>
        /// <returns>成功返回true,失败返回false</returns>
        [DllImport(DLL_USER32)]
        internal static extern bool EnumChildWindows(IntPtr hWndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);



        [DllImport(DLL_USER32)]
        internal static extern IntPtr GetParent(IntPtr hWnd);

        /// <summary>
        /// 判断窗口是否可见,可见返回true,不可见返回false
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <returns>可见返回true,不可见返回false</returns>
        [DllImport(DLL_USER32)]
        internal static extern bool IsWindowVisible(IntPtr hWnd);




        /// <summary>
        /// 获取指定窗口句柄的窗口标题
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="lptrString">存放窗口标题的StringBuilder</param>
        /// <param name="nMaxCount">窗口标题最大字符数</param>
        /// <returns></returns>
        [DllImport(DLL_USER32)]
        internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lptrString, int nMaxCount);

        /// <summary>
        /// 获取指定窗口句柄的窗口标题
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="lptrString">存放窗口标题的StringBuilder</param>
        /// <param name="nMaxCount">窗口标题最大字符数</param>
        /// <returns></returns>
        [DllImport(DLL_USER32)]
        internal static extern int GetWindowTextA(IntPtr hWnd, StringBuilder lptrString, int nMaxCount);

        /// <summary>
        /// 获取指定窗口句柄的窗口标题长度
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <returns>窗口标题长度</returns>
        [DllImport(DLL_USER32)]
        internal static extern int GetWindowTextLength(IntPtr hWnd);

        /// <summary>
        /// 获取指定窗口句柄的类名
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="lpString">存放窗口类名的StringBuilder</param>
        /// <param name="nMaxCount">最大字符数</param>
        /// <returns></returns>
        [DllImport(DLL_USER32)]
        internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpString, int nMaxCount);





        [DllImport(DLL_USER32)]
        internal static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab);


        /// <summary>
        /// 获取指定窗口句柄对应窗口的位置和尺寸
        /// </summary>
        /// <param name="hWnd">窗口句柄</param>
        /// <param name="rect">窗口的位置和尺寸</param>
        /// <returns>获取成功返回true,失败返回false</returns>
        [DllImport(DLL_USER32)]
        internal static extern bool GetWindowRect(IntPtr hWnd, ref WindowLPRECT rect);





        //https://msdn.microsoft.com/en-us/library/windows/desktop/ms633499(v=vs.85).aspx
        /// <summary>
        /// Retrieves a handle to the top-level window whose class name and window name match the specified strings. This function does not search child windows. This function does not perform a case-sensitive search.
        /// To search child windows, beginning with a specified child window, use the FindWindowEx function.
        /// If the lpWindowName parameter is not NULL, FindWindow calls the GetWindowText function to retrieve the window name for comparison. For a description of a potential problem that can arise, see the Remarks for GetWindowText.
        /// </summary>
        /// <param name="lpClassName">
        /// 窗口类名,约等于进程名
        /// The class name or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be in the low-order word of lpClassName; the high-order word must be zero.
        /// If lpClassName points to a string, it specifies the window class name. The class name can be any name registered with RegisterClass or RegisterClassEx, or any of the predefined control-class names.
        /// If lpClassName is NULL, it finds any window whose title matches the lpWindowName parameter.</param>
        /// <param name="lpWindowName">
        /// 窗口标题名称
        /// The window name (the window's title). If this parameter is NULL, all window names match</param>
        /// <returns>Type:
        /// Type: HWND
        /// If the function succeeds, the return value is a handle to the window that has the specified class name and window name.
        /// If the function fails, the return value is NULL.To get extended error information, call GetLastError.</returns>
        [DllImport(DLL_USER32, EntryPoint = "FindWindow", CharSet = CharSet.Unicode)]
        internal static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        /// <summary>
        /// 查找子窗口
        /// Retrieves a handle to a window whose class name and window name match the specified strings. The function searches child windows, beginning with the one following the specified child window. This function does not perform a case-sensitive search.
        /// If the lpszWindow parameter is not NULL, FindWindowEx calls the GetWindowText function to retrieve the window name for comparison. For a description of a potential problem that can arise, see the Remarks section of GetWindowText.
        /// An application can call this function in the following way.
        /// FindWindowEx(NULL, NULL, MAKEINTATOM(0x8000), NULL );
        /// Note that 0x8000 is the atom for a menu class. When an application calls this function, the function checks whether a context menu is being displayed that the application created.
        /// </summary>
        /// <param name="hwndParent">A handle to the parent window whose child windows are to be searched.
        /// If hwndParent is NULL, the function uses the desktop window as the parent window.The function searches among windows that are child windows of the desktop.
        /// If hwndParent is HWND_MESSAGE, the function searches all message-only windows.</param>
        /// <param name="hwndChildAfter">A handle to a child window. The search begins with the next child window in the Z order. The child window must be a direct child window of hwndParent, not just a descendant window.
        /// If hwndChildAfter is NULL, the search begins with the first child window of hwndParent.
        /// Note that if both hwndParent and hwndChildAfter are NULL, the function searches all top-level and message-only windows</param>
        /// <param name="lpszClass">The class name or a class atom created by a previous call to the RegisterClass or RegisterClassEx function. The atom must be placed in the low-order word of lpszClass; the high-order word must be zero.
        /// If lpszClass is a string, it specifies the window class name. The class name can be any name registered with RegisterClass or RegisterClassEx, or any of the predefined control-class names, or it can be MAKEINTATOM(0x8000). In this latter case, 0x8000 is the atom for a menu class. For more information, see the Remarks section of this topic</param>
        /// <param name="lpszWindow">The window name (the window's title). If this parameter is NULL, all window names match.</param>
        /// <returns>ype:
        /// Type: HWND
        ///  If the function succeeds, the return value is a handle to the window that has the specified class and window names.
        ///  If the function fails, the return value is NULL.To get extended error information, call GetLastError.</returns>
        [DllImport(DLL_USER32, EntryPoint = "FindWindow", CharSet = CharSet.Unicode)]
        internal static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

        /// <summary>
        /// 根据句柄查询该句柄对应的进程ID
        /// </summary>
        /// <param name="hwnd">应用程序句柄</param>
        /// <param name="processId">进程ID</param>
        /// <returns>创建窗口的线程ID</returns>
        [DllImport(DLL_USER32)]
        internal static extern int GetWindowThreadProcessId(IntPtr hwnd, out int processId);

        /// <summary>
        /// 最小化窗口在任务栏闪烁一次
        /// </summary>
        /// <param name="hwnd">窗口句柄</param>
        /// <param name="bInvert">true:表示窗口从一个状态闪烁到另一个状态;false:表示窗口恢复到初始状态（可能是激活的也可能是非激活的）</param>
        /// <returns>表示调用FlashWindow函数之前窗口的活动状态，若指定窗口在调用函数之前是激活的，那么返回非零值，否则返回零值</returns>
        [DllImport(DLL_USER32)]
        internal static extern bool FlashWindow(IntPtr hwnd, bool bInvert);

        /// <summary>
        /// 进程无焦点时，任务栏最小化窗口闪烁
        /// </summary>
        /// <param name="pwfi">窗口闪烁信息</param>
        /// <returns>返回调用 FlashWindowEx 函数之前指定窗口状态。如果调用之前窗口标题是活动的，返回值为非零值</returns>
        [DllImport(DLL_USER32)]
        [return: MarshalAs(UnmanagedType.Bool)]
        internal static extern bool FlashWindowEx(ref FLASHWINFO pwfi);

        /// <summary>
        /// 刷新系统缓存图标
        /// Notifies the system of an event that an application has performed. An application should use this function if it performs an action that may affect the Shell. 
        /// </summary>
        /// <param name="wEventId">Describes the event that has occurred. Typically, only one event is specified at a time. If more than one event is specified, the values contained in the dwItem1 and dwItem2 parameters must be the same, respectively, for all specified events. This parameter can be one or more of the following values. </param>
        /// <param name="uFlags">Flags that indicate the meaning of the dwItem1 and dwItem2 parameters. The uFlags parameter must be one of the following values.</param>
        /// <param name="dwItem1">First event-dependent value. </param>
        /// <param name="dwItem2">Second event-dependent value.</param>
        [DllImport(DLL_SHELL32, EntryPoint = "SHChangeNotify", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
        internal static extern void SHChangeNotify(Int32 wEventId, UInt32 uFlags, IntPtr dwItem1, IntPtr dwItem2);



        #region 最小化窗口在任务栏闪烁
        /// <summary>
        /// Stop flashing. The system restores the window to its original stae.
        /// </summary>
        internal const uint FLASHW_STOP = 0;

        /// <summary>
        /// Flash the window caption.
        /// </summary>
        internal const uint FLASHW_CAPTION = 1;

        /// <summary>
        /// Flash the taskbar button.
        /// </summary>
        internal const uint FLASHW_TRAY = 2;

        /// <summary>
        /// Flash both the window caption and taskbar button.
        /// This is equivalent to setting the FLASHW_CAPTION | FLASHW_TRAY flags.
        /// </summary>
        internal const uint FLASHW_ALL = 3;

        /// <summary>
        /// Flash continuously, until the FLASHW_STOP flag is set.
        /// </summary>
        internal const uint FLASHW_TIMER = 4;

        /// <summary>
        /// Flash continuously until the window comes to the foreground.
        /// </summary>
        internal const uint FLASHW_TIMERNOFG = 12;

        /// <summary>
        /// A boolean value indicating whether the application is running on Windows 2000 or later.
        /// </summary>
        internal static bool Win2000OrLater
        {
            get { return System.Environment.OSVersion.Version.Major >= 5; }
        }

        /// <summary>
        /// 创建窗口闪烁对象信息
        /// </summary>
        /// <param name="handle">窗口够本</param>
        /// <param name="flags">The Flash Status.</param>
        /// <param name="count">次数</param>
        /// <param name="timeout">The rate at which the Window is to be flashed, in milliseconds. If Zero, the function uses the default cursor blink rate.</param>
        /// <returns></returns>
        private static FLASHWINFO Create_FLASHWINFO(IntPtr handle, uint flags, uint count, uint timeout)
        {
            FLASHWINFO fi = new FLASHWINFO();
            fi.cbSize = Convert.ToUInt32(System.Runtime.InteropServices.Marshal.SizeOf(fi));
            fi.hwnd = handle;
            fi.dwFlags = flags;
            fi.uCount = count;
            fi.dwTimeout = timeout;
            return fi;
        }

        /// <summary>
        /// 任务栏窗口闪烁直到该窗口接收到焦点为止
        /// </summary>
        /// <param name="hwnd">窗口句柄</param>
        /// <returns></returns>
        internal static bool Flash(IntPtr hwnd)
        {
            // Make sure we're running under Windows 2000 or later
            if (Win2000OrLater)
            {
                FLASHWINFO fi = Create_FLASHWINFO(hwnd, FLASHW_ALL | FLASHW_TIMERNOFG, uint.MaxValue, 0);
                return FlashWindowEx(ref fi);
            }
            return false;
        }

        /// <summary>
        /// Flash the specified Window (form) for the specified number of times
        /// </summary>
        /// <param name="hwnd">窗口句柄</param>
        /// <param name="count">闪烁次数</param>
        /// <returns></returns>
        internal static bool Flash(IntPtr hwnd, uint count)
        {
            if (Win2000OrLater)
            {
                FLASHWINFO fi = Create_FLASHWINFO(hwnd, FLASHW_ALL, count, 0);
                return FlashWindowEx(ref fi);
            }
            return false;
        }

        /// <summary>
        /// Start Flashing the specified Window (form)
        /// </summary>
        /// <param name="hwnd">窗口句柄</param>
        /// <returns></returns>
        internal static bool Start(IntPtr hwnd)
        {
            if (Win2000OrLater)
            {
                FLASHWINFO fi = Create_FLASHWINFO(hwnd, FLASHW_ALL, uint.MaxValue, 0);
                return FlashWindowEx(ref fi);
            }
            return false;
        }

        /// <summary>
        /// Stop Flashing the specified Window (form)
        /// </summary>
        /// <param name="hwnd">窗口句柄</param>
        /// <returns></returns>
        internal static bool Stop(IntPtr hwnd)
        {
            if (Win2000OrLater)
            {
                FLASHWINFO fi = Create_FLASHWINFO(hwnd, FLASHW_STOP, uint.MaxValue, 0);
                return FlashWindowEx(ref fi);
            }
            return false;
        }
        #endregion


        #region 窗口淡入淡出
        /// <summary>
        /// 窗口淡入淡出
        /// </summary>
        /// <param name="hwnd">窗口句柄</param>
        /// <param name="dwTime">动画持续时间</param>
        /// <param name="dwFlags">动画类型(WindowAnimateType中的值按位枚举运算)</param>
        /// <returns>结果</returns>
        internal static bool WindowFadeInOut(IntPtr hwnd, int dwTime, int dwFlags)
        {
            return AnimateWindow(hwnd, dwTime, dwFlags);
        }

        /// <summary>
        /// 窗口淡入
        /// </summary>
        /// <param name="hwnd">窗口句柄</param>
        /// <param name="dwTime">动画持续时间</param>
        /// <param name="dwFlags">动画类型(WindowAnimateType中的值按位枚举运算)</param>
        /// <returns>结果</returns>
        internal static bool WindowFadeIn(IntPtr hwnd, int dwTime = 300, int dwFlags = WindowAnimateType.AW_BLEND)
        {
            return AnimateWindow(hwnd, dwTime, dwFlags);
        }

        /// <summary>
        /// 窗口淡出
        /// </summary>
        /// <param name="hwnd">窗口句柄</param>
        /// <param name="dwTime">动画持续时间</param>
        /// <param name="dwFlags">动画类型(WindowAnimateType中的值按位枚举运算)</param>
        /// <returns>结果</returns>
        internal static bool WindowFadeOut(IntPtr hwnd, int dwTime = 300, int dwFlags = WindowAnimateType.AW_SLIDE | WindowAnimateType.AW_HIDE | WindowAnimateType.AW_BLEND)
        {
            return AnimateWindow(hwnd, dwTime, dwFlags);
        }
        #endregion



        /// <summary>
        /// 设置应用程序的父窗口
        /// </summary>
        /// <param name="hWndChild">子窗口句柄</param>
        /// <param name="hWndNewParent">父窗口句柄</param>
        /// <returns>long</returns>
        [DllImport(DLL_USER32, SetLastError = true)]
        internal static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);

        /// <summary>
        /// 获取窗口样式风格-标题栏
        /// </summary>
        /// <param name="hwnd">handle to window</param>
        /// <param name="index"></param>
        /// <returns></returns>
        [DllImport(DLL_USER32, CharSet = CharSet.Auto)]
        internal static extern int GetWindowLong(IntPtr hwnd, int index);

        /********************************************************************************************************************************************************
         * SetWindowLongPtr nIndex
         * 常量字段名       值      意义
         * GWL_EXSTYLE      20-     设置新的 扩展窗口样式。
         * 
         * 
         * GWL_HINSTANCE    -6      设置新的应用程序实例句柄。
         * GWL_HWNDPARENT   -8      设置顶级窗口的新所有者。
         * GWL_ID           -12     设置子窗口的新标识符。 窗口不能是顶级窗口。
         * GWL_STYLE        -16     设置新的 窗口样式。
         * GWL_USERDATA     -21     设置与窗口关联的用户数据。 此数据供创建窗口的应用程序使用。 其值最初为零。
         * GWL_WNDPROC      -4      设置窗口过程的新地址。
         * 如果窗口不属于与调用线程相同的进程，则无法更改此属性。
         * 
         *  
         * 当 hWnd 参数标识对话框时，也可以使用以下值。
         * 
         * 常量字段名       值                                    意义
         * DWL_DLGPROC      DWLP_MSGRESULT + sizeof（LRESULT）    设置对话框过程的新地址。
         * DWL_MSGRESULT    0                                     设置在对话框过程中处理的消息的返回值。
         * DWL_USER         DWLP_DLGPROC + sizeof（DLGPROC）      设置应用程序专用的新额外信息，例如句柄或指针。
         * 
         ********************************************************************************************************************************************************/
        /// <summary>
        /// 更改指定窗口的属性。
        /// 如:设置窗口样式风格-标题栏,移除应用启动的程序窗口边框.
        /// 返回值:如果函数成功，则返回值为指定的 32 位整数的上一个值。
        /// 如果函数失败，则返回值为零。 若要获取扩展的错误信息，请调用 GetLastError。
        /// 如果指定的 32 位整数的上一个值为零，并且函数成功，则返回值为零，但该函数未清除最后的错误信息。 这使得难以确定成功或失败。 
        /// 若要解决此问题，应先调用 SetLastError 0，然后再调用 setWindowLong，以清除最后一个错误信息。 然后，函数失败将由返回值零和 GetLastError 非零的结果指示。
        /// </summary>
        /// <param name="hwnd">要移除边框的应用程序句柄</param>
        /// <param name="nIndex">要设置的值的从零开始的偏移量。 有效值介于零到额外窗口内存的字节数之间，减去整数的大小。 若要设置任何其他值，请指定以下值之一。</param>
        /// <param name="dwNewLong">边框值</param>
        /// <returns>long</returns>
        [DllImport(DLL_USER32, EntryPoint = "SetWindowLongPtr", SetLastError = true)]
        internal static extern int SetWindowLongPtr(IntPtr hwnd, int nIndex, int dwNewLong);


        /// <summary>
        /// 设置窗口位置
        /// </summary>
        /// <param name="hwnd">handle to window</param>
        /// <param name="hWndInsertAfter"></param>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="cx"></param>
        /// <param name="cy"></param>
        /// <param name="flags"></param>
        /// <returns></returns>
        [DllImport(DLL_USER32, CharSet = CharSet.Auto)]
        internal static extern bool SetWindowPos(IntPtr hwnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint flags);

        /// <summary>
        /// 移动应用程序窗口位置
        /// </summary>
        /// <param name="hwnd">程序窗口句柄</param>
        /// <param name="x">x坐标</param>
        /// <param name="y">y坐标</param>
        /// <param name="cx">宽度</param>
        /// <param name="cy">高度</param>
        /// <param name="repaint">是否修正</param>
        /// <returns>bool</returns>
        [DllImport(DLL_USER32, EntryPoint = "MoveWindow", SetLastError = true)]
        internal static extern bool MoveWindow(IntPtr hwnd, int x, int y, int cx, int cy, bool repaint);

        /// <summary>
        /// 启用dpi缩放
        /// </summary>
        /// <returns></returns>
        [DllImport(DLL_USER32)]
        internal static extern bool SetProcessDPIAware();

        /// <summary>
        /// 显示窗口
        /// </summary>
        /// <param name="hWnd"></param>
        /// <param name="nCmdShow">0:隐藏;1:显示</param>
        /// <returns></returns>
        [DllImport(DLL_USER32)]
        public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);


        /// <summary>
        /// 窗口淡入淡出
        /// </summary>
        /// <param name="hwnd">handle to window</param>
        /// <param name="dwTime">duration of animation</param>
        /// <param name="dwFlags">animation type</param>
        /// <returns></returns>
        [DllImport(DLL_USER32, CharSet = CharSet.Auto)]
        internal static extern bool AnimateWindow(IntPtr hwnd, int dwTime, int dwFlags);


        /// <summary>
        /// 判断系统是否认为指定的应用程序未响应.如果应用程序未等待输入、不在启动处理中，并且未在内部超时期限内调用PeekMessage,则被识为不响应.
        /// [如果窗口停止响应则返回true;否则返回false]
        /// </summary>
        /// <param name="hwnd">窗口句柄</param>
        /// <returns>如果窗口停止响应则返回true;否则返回false</returns>
        [DllImport(DLL_USER32)]
        internal static extern bool IsHungAppWindow(IntPtr hwnd);


        /// <summary>
        /// 向指定窗口发送消息
        /// </summary>
        /// <param name="hwnd">接收消息的窗口句柄</param>
        /// <param name="wMsg">要发送的消息</param>
        /// <param name="wParam">其它的消息特定信息</param>
        /// <param name="lParam">其它的消息特定信息</param>
        /// <returns>返回值指定消息处理的结果,这取决于发送的消息</returns>
        [DllImport(DLL_USER32)]
        internal static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);




    }


}
